package com.example.lexiangbackend.controller;

import com.example.lexiangbackend.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/api/files")
@CrossOrigin(origins = {"http://localhost:8080", "http://127.0.0.1:8080"}, allowCredentials = "true")
public class FileAccessController {

    @Value("${file.upload.path}")
    private String uploadBasePath;
    
    @Autowired
    private JwtUtil jwtUtil;
    
    private static final int CHUNK_SIZE = 32768; // 32KB chunks
    private static final long TOKEN_EXPIRE_MINUTES = 5; // 5分钟过期
    private static final Map<String, TokenInfo> activeTokens = new ConcurrentHashMap<>();
    
    /**
     * 测试接口
     */
    @GetMapping("/test")
    public ResponseEntity<String> test() {
        return ResponseEntity.ok("FileAccessController is working!");
    }
    
    /**
     * 测试音频文件访问接口（简化版）
     */
    @GetMapping("/songs/{filename:.+}")
    public ResponseEntity<Resource> testAudioFile(@PathVariable String filename) {
        try {
            System.out.println("收到音频文件请求: " + filename);
            
            // 构建文件路径
            Path filePath = Paths.get(uploadBasePath, "songs", filename).toAbsolutePath().normalize();
            System.out.println("音频文件完整路径: " + filePath.toString());
            
            // 检查文件是否存在
            boolean exists = Files.exists(filePath);
            System.out.println("文件是否存在: " + exists);
            
            if (!exists) {
                System.err.println("音频文件不存在: " + filePath.toString());
                return ResponseEntity.notFound().build();
            }
            
            // 检查是否是文件
            if (!Files.isRegularFile(filePath)) {
                System.err.println("路径不是文件: " + filePath.toString());
                return ResponseEntity.notFound().build();
            }
            
            Resource resource = new UrlResource(filePath.toUri());
            if (!resource.exists() || !resource.isReadable()) {
                System.err.println("文件无法读取: " + filePath.toString());
                return ResponseEntity.notFound().build();
            }
            
            // 获取文件内容类型
            String contentType = Files.probeContentType(filePath);
            if (contentType == null) {
                contentType = "audio/mpeg"; // 默认为mp3格式
            }
            
            System.out.println("音频文件访问成功: " + filename + ", 类型: " + contentType + ", 大小: " + resource.contentLength());
            
            return ResponseEntity.ok()
                    .contentType(MediaType.parseMediaType(contentType))
                    .header(HttpHeaders.CACHE_CONTROL, "public, max-age=31536000") // 缓存1年
                    .header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + filename + "\"")
                    .body(resource);
            
        } catch (Exception e) {
            System.err.println("访问音频文件时出错: " + e.getMessage());
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    // 存储token信息
    private static class TokenInfo {
        public final String filename;
        public final long expireTime;
        public final String sessionId;
        
        public TokenInfo(String filename, long expireTime, String sessionId) {
            this.filename = filename;
            this.expireTime = expireTime;
            this.sessionId = sessionId;
        }
        
        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }
    
    /**
     * 生成安全的音频访问令牌
     */
    @PostMapping("/audio-token")
    public ResponseEntity<?> generateAudioToken(@RequestBody Map<String, String> request, 
                                                HttpServletRequest httpRequest) {
        try {
            String filename = request.get("filename");
            if (filename == null || filename.trim().isEmpty()) {
                return ResponseEntity.badRequest().body(Map.of("error", "缺少文件名"));
            }
            
            // 验证文件是否存在
            Path filePath = Paths.get(uploadBasePath, "songs", filename).toAbsolutePath().normalize();
            if (!Files.exists(filePath)) {
                return ResponseEntity.notFound().build();
            }
            
            // 生成唯一的访问令牌
            String token = generateSecureToken();
            String sessionId = httpRequest.getSession().getId();
            long expireTime = System.currentTimeMillis() + (TOKEN_EXPIRE_MINUTES * 60 * 1000);
            
            // 存储令牌信息
            activeTokens.put(token, new TokenInfo(filename, expireTime, sessionId));
            
            // 清理过期令牌
            cleanExpiredTokens();
            
            System.out.println("生成音频访问令牌: " + token + " for " + filename);
            
            return ResponseEntity.ok(Map.of(
                "token", token,
                "url", "/api/files/secure-audio/" + token,
                "expires", expireTime
            ));
            
        } catch (Exception e) {
            System.err.println("生成音频令牌时出错: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Map.of("error", "令牌生成失败"));
        }
    }
    
    /**
     * 安全的音频流传输 - 防下载设计
     */
    @GetMapping("/secure-audio/{token}")
    public void getSecureAudio(@PathVariable String token,
                               @RequestParam(required = false) Integer chunk,
                               HttpServletRequest request,
                               HttpServletResponse response) throws IOException {
        
        // 验证令牌
        TokenInfo tokenInfo = activeTokens.get(token);
        if (tokenInfo == null || tokenInfo.isExpired()) {
            System.err.println("无效或过期的音频令牌: " + token);
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "访问令牌无效或已过期");
            return;
        }
        
        // 验证会话
        String currentSessionId = request.getSession().getId();
        if (!tokenInfo.sessionId.equals(currentSessionId)) {
            System.err.println("会话不匹配，令牌: " + token);
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "会话验证失败");
            return;
        }
        
        // 防爬虫检测
        if (!isValidRequest(request)) {
            System.err.println("检测到可疑请求，拒绝访问");
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "请求被拒绝");
            return;
        }
        
        String filename = tokenInfo.filename;
        Path filePath = Paths.get(uploadBasePath, "songs", filename).toAbsolutePath().normalize();
        File file = filePath.toFile();
        
        if (!file.exists()) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        
        // 设置防下载的响应头
        response.setContentType("application/octet-stream");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "0");
        response.setHeader("X-Content-Type-Options", "nosniff");
        response.setHeader("X-Frame-Options", "DENY");
        response.setHeader("Content-Security-Policy", "default-src 'self'");
        
        // 处理分块请求
        if (chunk != null) {
            handleChunkRequest(file, chunk, response);
        } else {
            handleStreamRequest(file, request, response);
        }
    }
    
    /**
     * 获取音频元数据（不包含实际音频数据）
     */
    @PostMapping("/audio-info")
    public ResponseEntity<?> getAudioInfo(@RequestBody Map<String, String> request) {
        try {
            String filename = request.get("filename");
            if (filename == null) {
                return ResponseEntity.badRequest().body(Map.of("error", "缺少文件名"));
            }
            
            Path filePath = Paths.get(uploadBasePath, "songs", filename).toAbsolutePath().normalize();
            File file = filePath.toFile();
            
            if (!file.exists()) {
                return ResponseEntity.notFound().build();
            }
            
            long fileSize = file.length();
            int totalChunks = (int) Math.ceil((double) fileSize / CHUNK_SIZE);
            
            Map<String, Object> info = new HashMap<>();
            info.put("filename", filename);
            info.put("size", fileSize);
            info.put("chunks", totalChunks);
            info.put("chunkSize", CHUNK_SIZE);
            
            return ResponseEntity.ok(info);
            
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Map.of("error", "获取文件信息失败"));
        }
    }
    
    /**
     * 处理分块请求
     */
    private void handleChunkRequest(File file, int chunkIndex, HttpServletResponse response) throws IOException {
        long fileSize = file.length();
        long startByte = (long) chunkIndex * CHUNK_SIZE;
        
        if (startByte >= fileSize) {
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }
        
        long endByte = Math.min(startByte + CHUNK_SIZE - 1, fileSize - 1);
        long chunkLength = endByte - startByte + 1;
        
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + fileSize);
        response.setContentLengthLong(chunkLength);
        
        try (RandomAccessFile raf = new RandomAccessFile(file, "r");
             OutputStream out = response.getOutputStream()) {
            
            raf.seek(startByte);
            byte[] buffer = new byte[(int) chunkLength];
            int bytesRead = raf.read(buffer);
            
            if (bytesRead > 0) {
                // 简单的数据混淆（非加密，但增加提取难度）
                obfuscateData(buffer, chunkIndex);
                out.write(buffer, 0, bytesRead);
            }
        }
    }
    
    /**
     * 处理流式请求
     */
    private void handleStreamRequest(File file, HttpServletRequest request, HttpServletResponse response) throws IOException {
        long fileLength = file.length();
        String rangeHeader = request.getHeader("Range");
        
        if (rangeHeader == null) {
            // 完整文件请求 - 但进行流式传输
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentLengthLong(fileLength);
            streamFileInChunks(file, 0, fileLength - 1, response);
        } else {
            // Range请求
            handleRangeRequest(file, rangeHeader, response);
        }
    }
    
    /**
     * 流式传输文件
     */
    private void streamFileInChunks(File file, long start, long end, HttpServletResponse response) throws IOException {
        try (RandomAccessFile raf = new RandomAccessFile(file, "r");
             OutputStream out = response.getOutputStream()) {
            
            raf.seek(start);
            long remaining = end - start + 1;
            byte[] buffer = new byte[CHUNK_SIZE];
            int chunkIndex = 0;
            
            while (remaining > 0) {
                int bytesToRead = (int) Math.min(CHUNK_SIZE, remaining);
                int bytesRead = raf.read(buffer, 0, bytesToRead);
                
                if (bytesRead == -1) break;
                
                // 数据混淆
                obfuscateData(buffer, chunkIndex++);
                
                out.write(buffer, 0, bytesRead);
                remaining -= bytesRead;
                
                // 添加小延迟，防止快速下载
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
    }
    
    /**
     * 处理Range请求
     */
    private void handleRangeRequest(File file, String rangeHeader, HttpServletResponse response) throws IOException {
        long fileLength = file.length();
        
        if (!rangeHeader.startsWith("bytes=")) {
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }
        
        String range = rangeHeader.substring(6);
        String[] ranges = range.split("-");
        
        long start = 0;
        long end = fileLength - 1;
        
        try {
            if (ranges.length >= 1 && !ranges[0].isEmpty()) {
                start = Long.parseLong(ranges[0]);
            }
            if (ranges.length >= 2 && !ranges[1].isEmpty()) {
                end = Long.parseLong(ranges[1]);
            }
        } catch (NumberFormatException e) {
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }
        
        if (start > end || start < 0 || end >= fileLength) {
            response.setHeader("Content-Range", "bytes */" + fileLength);
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }
        
        long contentLength = end - start + 1;
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        response.setContentLengthLong(contentLength);
        response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
        
        streamFileInChunks(file, start, end, response);
    }
    
    /**
     * 数据混淆（可逆转换）
     */
    private void obfuscateData(byte[] data, int key) {
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) (data[i] ^ (key & 0xFF));
        }
    }
    
    /**
     * 验证请求合法性
     */
    private boolean isValidRequest(HttpServletRequest request) {
        // 检查User-Agent
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null || userAgent.contains("wget") || userAgent.contains("curl")) {
            return false;
        }
        
        // 检查Referer
        String referer = request.getHeader("Referer");
        if (referer == null || (!referer.contains("localhost:8080") && !referer.contains("127.0.0.1:8080"))) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 生成安全令牌
     */
    private String generateSecureToken() {
        SecureRandom random = new SecureRandom();
        byte[] bytes = new byte[32];
        random.nextBytes(bytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
    }
    
    /**
     * 清理过期令牌
     */
    private void cleanExpiredTokens() {
        activeTokens.entrySet().removeIf(entry -> entry.getValue().isExpired());
    }
    
    /**
     * 访问封面图片
     */
    @GetMapping("/covers/{filename:.+}")
    public ResponseEntity<Resource> getCoverImage(@PathVariable String filename) {
        try {
            System.out.println("请求封面图片: " + filename);
            
            Path filePath = Paths.get(uploadBasePath, "covers", filename).toAbsolutePath().normalize();
            Resource resource = new UrlResource(filePath.toUri());
            
            if (resource.exists() && resource.isReadable()) {
                String contentType = getImageContentType(filename);
                
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.parseMediaType(contentType));
                headers.setCacheControl("max-age=31536000");
                
                return ResponseEntity.ok()
                        .headers(headers)
                        .body(resource);
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (MalformedURLException e) {
            return ResponseEntity.badRequest().build();
        }
    }
    
    /**
     * 获取文件信息
     */
    @GetMapping("/info/{type}/{filename:.+}")
    public ResponseEntity<?> getFileInfo(@PathVariable String type, @PathVariable String filename) {
        try {
            String directory = type.equals("covers") ? "covers" : "songs";
            Path filePath = Paths.get(uploadBasePath, directory, filename).toAbsolutePath().normalize();
            File file = filePath.toFile();
            
            Map<String, Object> fileInfo = new HashMap<>();
            fileInfo.put("filename", filename);
            fileInfo.put("type", type);
            fileInfo.put("exists", file.exists());
            
            if (file.exists()) {
                fileInfo.put("size", file.length());
                fileInfo.put("path", filePath.toString());
            }
            
            return ResponseEntity.ok(fileInfo);
        } catch (Exception e) {
            return ResponseEntity.badRequest().build();
        }
    }
    

    
    // ================================ 私有方法 ================================
    
    /**
     * 根据文件扩展名获取图片Content-Type
     */
    private String getImageContentType(String filename) {
        String extension = filename.toLowerCase();
        
        if (extension.endsWith(".jpg") || extension.endsWith(".jpeg")) {
            return "image/jpeg";
        } else if (extension.endsWith(".png")) {
            return "image/png";
        } else if (extension.endsWith(".gif")) {
            return "image/gif";
        } else if (extension.endsWith(".webp")) {
            return "image/webp";
        } else {
            return "image/jpeg";
        }
    }
} 